﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Kinect;
using Microsoft.Speech.Recognition;
using System.Threading;
using System.IO;
using Microsoft.Speech.AudioFormat;
using System.ComponentModel;

namespace DTW_Beta
{
    static class AudioListener
    {
        public static bool IS_AUDIOLISTENER_ON;

        public delegate void AudioListenedEventHandler(object sender, string a);
        public static event AudioListenedEventHandler AudioListened;

        private static readonly BackgroundWorker _worker = new BackgroundWorker();

        public static void Start()
        {
            _worker.DoWork += _worker_DoWork;
            _worker.ProgressChanged += new ProgressChangedEventHandler(_worker_ProgressChanged);
            _worker.WorkerReportsProgress = true;
            _worker.WorkerSupportsCancellation = true;
            _worker.RunWorkerAsync();

            IS_AUDIOLISTENER_ON = true;
        }

        public static void Stop()
        {
            _worker.CancelAsync();
            sre.RecognizeAsyncCancel();

            IS_AUDIOLISTENER_ON = false;
        }

        private static void _worker_DoWork(object sender, DoWorkEventArgs e)
        {
            InitializeAudio();
        }

        static void _worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            string s = ((string)e.UserState);

            AudioListened(null, s);
        }

        private static SpeechRecognitionEngine sre;

        private static void InitializeAudio()
        {
            KinectSensor sensor = (from sensorToCheck in KinectSensor.KinectSensors where sensorToCheck.Status == KinectStatus.Connected select sensorToCheck).FirstOrDefault();
            if (sensor == null)
            {
                // No kinect sensor available ..
                return;
            }

            sensor.Start();

            KinectAudioSource source = sensor.AudioSource;
            source.EchoCancellationMode = EchoCancellationMode.None; // No AEC for this sample
            source.AutomaticGainControlEnabled = false; // Important to turn this off for speech recognition

            RecognizerInfo ri = GetKinectRecognizer();

            if (ri == null)
            {
                // Could not find Kinect speech recognizer. Please refer to the sample requirements.
                return;
            }

            // NOTE: Need to wait 4 seconds for device to be ready right after initialization
            int wait = 4;
            while (wait > 0)
            {
                Console.Write("Device will be ready for speech recognition in {0} second(s).\r", wait--);
                Thread.Sleep(1000);
            }

            sre = new SpeechRecognitionEngine(ri.Id);
          
            var start_stop = new Choices();
            start_stop.Add("start");
            start_stop.Add("stop");

            var gb = new GrammarBuilder { Culture = ri.Culture };
            var grammarBuilder = new GrammarBuilder();

            grammarBuilder.Append(start_stop);

            // Create the actual Grammar instance, and then load it into the speech recognizer.+
            var g = new Grammar(grammarBuilder);

            sre.LoadGrammar(g);
            sre.SpeechRecognized += SreSpeechRecognized;
            sre.SpeechHypothesized += SreSpeechHypothesized;
            sre.SpeechRecognitionRejected += SreSpeechRecognitionRejected;

            using (Stream s = source.Start())
            {
                sre.SetInputToAudioStream(
                    s, new SpeechAudioFormatInfo(EncodingFormat.Pcm, 16000, 16, 1, 32000, 2, null));

                sre.RecognizeAsync(RecognizeMode.Multiple);

                Thread.Sleep(10000000);
            }
            
        }

        private static RecognizerInfo GetKinectRecognizer()
        {
            Func<RecognizerInfo, bool> matchingFunc = r =>
            {
                string value;
                r.AdditionalInfo.TryGetValue("Kinect", out value);
                return "True".Equals(value, StringComparison.InvariantCultureIgnoreCase) && "en-US".Equals(r.Culture.Name, StringComparison.InvariantCultureIgnoreCase);
            };
            return SpeechRecognitionEngine.InstalledRecognizers().Where(matchingFunc).FirstOrDefault();
        }

        private static void SreSpeechRecognitionRejected(object sender, SpeechRecognitionRejectedEventArgs e)
        {
            //Speech rejected
        }

        private static void SreSpeechHypothesized(object sender, SpeechHypothesizedEventArgs e)
        {
            // Speech Hypothesized: e.Result.Text;
        }

        private static void SreSpeechRecognized(object sender, SpeechRecognizedEventArgs e)
        {
            if (e.Result.Confidence >= 0.6)
            {
                _worker.ReportProgress(0, e.Result.Text);
            }
        }
    }
}
